#include<cairo.h>
#include<cairo-pdf.h>
#include<cairo-ps.h>
#include<cairo-xlib.h>
#include<X11/Xlib.h>

#include<libxml/xmlmemory.h>
#include<libxml/parser.h>
#include<libxml/tree.h>

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

#define oops(s) { perror((s)); exit(EXIT_FAILURE); }
#define MALLOC(s,t) if(((s) = malloc(t)) == NULL) { oops("error: malloc() "); }

int sizex,sizey;
char ** name;
void paint(cairo_surface_t *cs,int **a,int n)
{
	cairo_t *c;
	int i=0;
	double r,g,b;
	
	c=cairo_create(cs);
	srand( (unsigned)time( NULL ) );
	
	cairo_rectangle(c, 0.0, 0.0, sizex, sizey);
	cairo_set_source_rgb(c, 1.0, 1.0, 1.0);
	cairo_fill(c);
	i=0;
	while(i<n)
	{
	cairo_rectangle(c,a[i][0],a[i][1],a[i][2]-a[i][0],a[i][3]-a[i][1]);
	r = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
	g = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
	b = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) )/2;
	cairo_set_source_rgba(c,r,g,b,0.5);
	cairo_fill(c);
	cairo_move_to(c,(a[i][0] + a[i][2])/2,(a[i][1]+a[i][3])/2);
	cairo_set_source_rgb(c, 0.0, 0.0, 0.0);
	cairo_show_text(c,name[i]);
	i++;
	}
	cairo_set_line_width(c,0.5);
	cairo_stroke(c);
	cairo_show_page(c);
	cairo_destroy(c);
}

void showxlib(const char *fname,int **a,int n)
{
	Display *dpy;
	Window rootwin;
	Window win;
	XEvent e;
	int scr;
	cairo_surface_t *cs;

	if(!(dpy=XOpenDisplay(NULL))) {
		fprintf(stderr, "ERROR: Could not open display\n");
		exit(1);
	}

	scr=DefaultScreen(dpy);
	rootwin=RootWindow(dpy, scr);

	win=XCreateSimpleWindow(dpy, rootwin, 1, 1, sizex, sizey, 0, 
			BlackPixel(dpy, scr), BlackPixel(dpy, scr));

	XStoreName(dpy, win, "oasp");
	XSelectInput(dpy, win, ExposureMask|ButtonPressMask);
	XMapWindow(dpy, win);
	cs=cairo_xlib_surface_create(dpy, win, DefaultVisual(dpy, 0), sizex, sizey);

	while(1) {
		XNextEvent(dpy, &e);
		if(e.type==Expose && e.xexpose.count<1) {
			paint(cs,a,n);
		} else if(e.type==ButtonPress) break;
	}

	cairo_surface_destroy(cs);
	XCloseDisplay(dpy);
}
void writepng(const char *fname,int **a,int n)
{
	cairo_surface_t *cs;

	cs=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sizex, sizey);

	paint(cs,a,n);
	cairo_surface_write_to_png(cs, fname);
	cairo_surface_destroy(cs);
}

void writepdf(const char *fname,int **a,int n)
{
	cairo_surface_t *cs;

	cs=cairo_pdf_surface_create(fname, sizex, sizey);

	paint(cs,a,n);

	cairo_surface_flush(cs);
	cairo_surface_destroy(cs);
}
void writeps(const char *fname,int **a,int n)
{
	cairo_surface_t *cs;

	cs=cairo_ps_surface_create(fname, sizex, sizey);

	paint(cs,a,n);

	cairo_surface_flush(cs);
	cairo_surface_destroy(cs);
}

/*void writesvg(const char *fname,int **a,int n)
{
	cairo_surface_t *cs;

	cs=cairo_svg_surface_create(fname, sizex, sizey);

	paint(cs,a,n);

	cairo_surface_flush(cs);
	cairo_surface_destroy(cs);
}*/
int main(int argc, char *argv[])
{
	char *p;
	void (*pf) (const char *,int **,int) ;
	int **coord;
	int n=0,i;
	        
	xmlNode *curnode;
	xmlDocPtr doc;
	xmlNode *root =NULL;	
/*----------- Select Appropriate function depending on argument */
	if(argc>2) {
		if((p=strchr(argv[2], '.'))) {
			++p;
			if(!strcmp(p, "png")) pf=&writepng; 
			else if(!strcmp(p, "pdf")) pf=&writepdf;
			else if(!strcmp(p, "ps")) pf=&writeps;
			/*else if(!strcmp(p, "svg")) pf=&writesvg;*/
			else pf=&writepng;
		} else pf=&writepdf;
	} else pf=&showxlib;
/*----------- Opening the XML Document -------------- */
	doc =xmlParseFile(argv[1]);

	if (doc == NULL) printf("error: could not parse.");

	/*Get the root element*/
	root = xmlDocGetRootElement(doc);
        if(!root || !root->name || xmlStrcmp(root->name,(const xmlChar *) "space") )
        {
                      xmlFreeDoc(doc);
		      return 1;
        }
	sizex=atoi((char *)xmlGetProp(root,(const xmlChar *)"x"));
	sizey=atoi((char *)xmlGetProp(root,(const xmlChar *)"y"));


	/* Find the Number of Rectangles */
	for(curnode=root->children;curnode!=NULL;curnode=curnode->next) if (curnode->type == XML_ELEMENT_NODE && !xmlStrcmp(curnode->name,(const xmlChar *) "rect")) n++;

	/* Allocate Enough Space*/
	MALLOC(coord, sizeof(int *) * n);
  	for (i = 0; i < n; i++) 
	{
    	MALLOC(coord[i], sizeof(int) * 4);
  	}
	MALLOC(name, sizeof(char *) * n);
  	for (i = 0; i < n; i++) 
	{
    	MALLOC(name[i], sizeof(char) * 100);
  	}
	/* For each element Rectangle */	
	n=0;
	for(curnode=root->children;curnode!=NULL;curnode=curnode->next)
	{
	
	if (curnode->type == XML_ELEMENT_NODE && !xmlStrcmp(curnode->name,(const xmlChar *) "rect"))
	{

		coord[n][0]=atoi((char *)xmlGetProp(curnode,(const xmlChar *)"x1"));
		coord[n][1]=atoi((char *)xmlGetProp(curnode,(const xmlChar *)"y1"));
		coord[n][2]=atoi((char *)xmlGetProp(curnode,(const xmlChar *)"x2"));
		coord[n][3]=atoi((char *)xmlGetProp(curnode,(const xmlChar *)"y2"));
		name[n]=(char *)xmlGetProp(curnode,(const xmlChar *)"name");
		if(coord[n][0]>sizex ||coord[n][1]>sizey ||coord[n][2]>sizex ||coord[n][3]>sizey) 
		{ 
			printf("error: Coordinates out of range\n");
			exit(EXIT_FAILURE);
		}
		n++;
	
	}
	}

	/*Call the required drafting function */
	pf(argv[2],coord,n);

	/* Free the Document & Parser allocated global variables */
	for (i = 0; i < n; i++) free(coord[i]);
  	free(coord);
	xmlFreeDoc(doc);
	xmlCleanupParser();
	return 0;
}
